package com.gitee.zhangchenyan.takin.service.server;

import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.gitee.zhangchenyan.takin.common.utils.ExceptionUtils;
import com.gitee.zhangchenyan.takin.common.utils.HttpServletUtils;
import com.gitee.zhangchenyan.takin.service.common.ServiceRequest;
import com.gitee.zhangchenyan.takin.service.common.ServiceResponse;
import com.gitee.zhangchenyan.takin.service.common.TakinTypeToken;
import org.springframework.context.annotation.Import;
import org.springframework.core.ResolvableType;
import lombok.extern.slf4j.Slf4j;
import lombok.val;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Type;

@Import(cn.hutool.extra.spring.SpringUtil.class)
@RestController
@Slf4j
@RequestMapping("/takin/service_server")
public class TakinServiceServerRpcController {

    @PostMapping("/execute")
    public ServiceResponse execute(@RequestBody ServiceRequest serviceRequest) {
        try {
            log.debug(String.format("rpc入参:%s", JSONObject.toJSONString(serviceRequest, SerializerFeature.WriteMapNullValue)));
            //身份处理
            String token = serviceRequest.getToken();
            if (StrUtil.isNotEmpty(token)) {
                HttpServletUtils.GetHttpServletRequest().setAttribute("token", token);
            }
            //将参数值和参数类型匹配对应
            Class<?>[] parameterType = serviceRequest.getParameterType();
            Object[] parameterValue = serviceRequest.getParameterValue();
            Class<?> aClass = Class.forName(serviceRequest.getClassName());
            //由于参数值是通过json转换的，这边拿到的参数值全是JSONObject类型。所以需要将参数值按照参数类型重新转换一遍。
            if (parameterType != null && parameterValue != null && parameterType.length == parameterValue.length) {
                Method method = aClass.getMethod(serviceRequest.getMethodName(), parameterType);
                for (int i = 0; i < parameterValue.length; i++) {
                    ResolvableType resolvableType = ResolvableType.forMethodParameter(method, i, aClass);
                    Type parameterizedType = new TakinTypeToken().getParameterizedTypeImpl(resolvableType);
                    val o = JSON.parseObject(JSONObject.toJSONString(parameterValue[i], SerializerFeature.WriteMapNullValue), parameterizedType);
                    parameterValue[i] = o;
                }
            }
            //新方式
            Object o = SpringUtil.getBean(aClass);

            //老方式弃用
            //先判断bean容器是否为空，如果为空则可以拿到请求参数的类名（全限定名）反射去创建对象CGLIB$CALLBACK_1 = {CglibAopProxy$StaticUnadvisedInterceptor@11988}
//            if (ServiceServerBean.SERVICE_BEAN.size() == 0) {//我这里逻辑没写，可以自己加(request.getClassName()传类的全限定名就可以)
//                throw new Exception("SERVICE_BEAN为空");
//            }
//            Object o = ServiceServerBean.SERVICE_BEAN.get(serviceRequest.getClassName());
            //根据请求传的bean的name去bean容器里查，没查到则通过类全限定名反射创建对象
            if (o == null) {
                throw new Exception(String.format("Spring未找到bean[%s]", serviceRequest.getClassName()));
            }
            Object result = null;
            if (parameterValue != null && parameterValue.length > 0) {
                Method method = aClass.getMethod(serviceRequest.getMethodName(), parameterType);
                result = method.invoke(o, parameterValue);
            } else {
                Method method = aClass.getMethod(serviceRequest.getMethodName());
                result = method.invoke(o);
            }

//            FastClass serviceFastClass = FastClass.create(aClass);
//            int methodIndex = serviceFastClass.getIndex(serviceRequest.getMethodName(), parameterType);
//            result = serviceFastClass.invoke(methodIndex, o, parameterValue);

            ServiceResponse success = ServiceResponse.Success(result);
            log.debug(String.format("rpc出参:%s", JSONObject.toJSONString(success, SerializerFeature.WriteMapNullValue)));
            return success;
        } catch (InvocationTargetException e) {
            String outputException = ExceptionUtils.outputStr(e.getTargetException(), "服务");
            return ServiceResponse.Error(outputException);
        } catch (Exception ex) {
            String outputException = ExceptionUtils.outputStr(ex, "rpc");
            return ServiceResponse.Error(outputException);
        }
    }
}
